namespace EyeOpen.Data.DataAnnotations.Nhibernate
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Reflection;

	using EyeOpen.Linq;

	using NHibernate.Mapping.ByCode;

	internal class CollectionMapper<T>
		where T : class
	{
		private readonly IPropertyContainerMapper<T> annotationsMap;
		private readonly IEnumerable<PropertyInfo> collectionProperties;

		public CollectionMapper(IPropertyContainerMapper<T> annotationsMap, IEnumerable<PropertyInfo> collectionProperties)
		{
			this.annotationsMap = annotationsMap;
			this.collectionProperties = collectionProperties;
		}

		public void Map()
		{
			var mapCollectionMethod = 
				GetType()
					.GetMethod("MapCollection", BindingFlags.NonPublic | BindingFlags.Instance);

			collectionProperties
				.ForEach(x => mapCollectionMethod.MakeGenericMethod(GetType(x.PropertyType)).Invoke(this, new object[] { x }));
		}

		private Type GetType(Type type)
		{
			return
				type
					.GetGenericArguments()
					.First();
		}

		private void MapCollection<TProperty>(PropertyInfo collection) 
			where TProperty : class
		{
			annotationsMap
				.Bag<TProperty>(
					collection.Name,
					m =>
						{
							m.Inverse(true);
							m.Access(GetAccessor(collection));
							m.Cascade(Cascade.Merge);
							m.Key(k => k.Column(typeof(T).Name + "Id"));
						}, 
						r => r.OneToMany());
		}

		private Accessor GetAccessor(PropertyInfo collection)
		{
			if (collection.CanWrite)
			{
				return Accessor.Property;
			}

			return Accessor.Field;
		}
	}
}